package seatiger.tomcat.catalina.connector;

import seatiger.tomcat.catalina.HttpRequest;
import seatiger.tomcat.catalina.Manager;
import seatiger.tomcat.catalina.Session;
import seatiger.tomcat.catalina.connector.http.MethodEnum;
import seatiger.tomcat.catalina.session.StandardSession;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * http 请求抽象类
 */
@Deprecated
public abstract class HttpRequestBase extends RequestBase implements HttpRequest, HttpServletRequest {

    protected Map<String, List<String>> headers = new ConcurrentHashMap<>();

    private String method;

    protected String queryString = null;

    protected String requestURI = null;

    /**
     * 判断是否已经解析过了
     */
    private volatile boolean parsed = false;

    /**
     * 解析请求参数  一个key 可能对应多个值
     */
    private Map<String,List<String>> parameters = new LinkedHashMap<>();


    /**
     * 本次请求 session ID
     */
    protected String requestedSessionId = null;

    /**
     * 本地请求的cookie
     */
    protected List<Cookie> cookies = new ArrayList();

    /**
     * 当前活跃的session
     */
    protected Session session = null;

    protected HttpRequestFacade facade = new HttpRequestFacade(this);

    /**
     * context的根路径
     */
    protected String contextPath = "";

    @Override
    public void addHeader(String name, String value) {
        name = name.toLowerCase();
        List<String> res = headers.computeIfAbsent(name, n -> new ArrayList<>());
        res.add(value);
    }

    @Override
    public void setMethod(String method) {
        this.method = method;
    }

    @Override
    public String getMethod() {
        return method;
    }

    @Override
    public void setQueryString(String query) {
        this.queryString = query;
    }

    @Override
    public void setRequestURI(String uri) {
        this.requestURI = uri;
    }

    @Override
    public String getRequestURI() {
        return requestURI;
    }

    @Override
    public String getQueryString() {
        return queryString;
    }

    @Override
    public String getHeader(String name) {
        name = name.toLowerCase();
        List<String> result = headers.getOrDefault(name, Collections.emptyList());
        return result.size() > 0 ? String.join(",", result) : null;
    }

    @Override
    public Enumeration getHeaders(String name) {
        name = name.toLowerCase();
        List<String> orDefault = headers.getOrDefault(name, Collections.emptyList());
        return Collections.enumeration(orDefault);
    }

    @Override
    public Enumeration getHeaderNames() {
        return Collections.enumeration(headers.keySet());
    }

    /**
     * 判断 值 是否可以转换成整数
     * @param name
     * @return
     */
    @Override
    public int getIntHeader(String name) {
        String header = getHeader(name);
        return header == null ? -1 : Integer.parseInt(header);
    }

    /**
     * 解析请求参数,保证多线程时 只解析一次
     */
    private void parseParameters(){
        if (parsed){
            return;
        }
        synchronized (parameters){
            if (parsed){
                return;
            }
            parseQueryString(getQueryString());
            parseBody();
            parsed = true;
        }
    }

    /**
     * 解析body数据
     */
    private void parseBody() {
        String contentType = getContentType();
        if (contentType == null){
            return;
        }
        List<String> strings = Arrays.asList(contentType.split(";"));
        if (MethodEnum.POST.name().equals(getMethod()) &&
                getContentLength() > 0 &&
                strings.contains("application/x-www-form-urlencoded")){
            //获取SocketInputStream
            try (ServletInputStream in = getInputStream()){
                byte[] bytes = new byte[getContentLength()];
                in.read(bytes);
                parseQueryString(new String(bytes));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 解析 queryString
     */
    private void parseQueryString(String queryString) {
        if (queryString != null){
            Arrays.stream(queryString.split("&")).forEach(str -> {
                if (str.indexOf("=") > 0){
                    String[] split = str.split("=");
                    List<String> strings = parameters.computeIfAbsent(split[0], k -> new ArrayList<>());
                    strings.add(split[1]);
                }
            });
        }
    }

    /**
     * 获取参数
     * @param name
     * @return
     */
    @Override
    public String getParameter(String name) {
        parseParameters();
        List<String> strings = parameters.get(name);
        return strings == null ? null : strings.get(0);
    }

    /**
     * 获取所有的参数key
     * @return
     */
    @Override
    public Enumeration getParameterNames() {
        parseParameters();
        return Collections.enumeration(parameters.keySet());
    }

    @Override
    public String[] getParameterValues(String name) {
        parseParameters();
        List<String> collect = parameters.values().stream().flatMap(s -> s.stream()).collect(Collectors.toList());
        return collect.toArray(new String[collect.size()]);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        parseParameters();
        Map<String, String[]> map = new HashMap<>();
        for (Map.Entry<String,List<String>> entry : parameters.entrySet()){
            String key = entry.getKey();
            List<String> value = entry.getValue();
            map.put(key,value.toArray(new String[value.size()]));
        }
        return map;
    }

    @Override
    public String getRequestedSessionId() {
        return requestedSessionId;
    }

    @Override
    public void setRequestedSessionId(String id) {
        this.requestedSessionId = id;
    }

    @Override
    public void addCookie(Cookie cookie) {
        cookies.add(cookie);
    }

    @Override
    public Cookie[] getCookies() {
        return cookies.size() > 0 ? cookies.toArray(new Cookie[cookies.size()]) : null;
    }

    /**
     * 获取session 如果没有创建一个
     * @param create
     * @return
     */
    @Override
    public HttpSession getSession(boolean create) {
        Manager manager;
        if (context == null || (manager = context.getManager()) == null){
            return null;
        }
        if (session != null){
            return session.getSession();
        }
        if (requestedSessionId != null){
            try {
                session = manager.findSession(requestedSessionId);
                if (session != null){
                    return session.getSession();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //创建新的session
        if (create){
            this.session = manager.createSession();
            return session.getSession();
        }
        return null;
    }

    @Override
    public HttpSession getSession() {
        return getSession(true);
    }

    @Override
    public void recycle() {
        super.recycle();
        cookies.clear();
        headers.clear();
        method = null;
        if (parameters != null) {
            parameters.clear();
        }
        parsed = false;
        queryString = null;
        requestedSessionId = null;
        requestURI = null;
        session = null;
    }

    @Override
    public ServletRequest getRequest() {
        return facade;
    }


    @Override
    public String getDecodedRequestURI() {
        return requestURI;
    }

    @Override
    public RequestDispatcher getRequestDispatcher(String path) {
        if (path ==null) {
            return null;
        }
        return context.getServletContext().getRequestDispatcher(path);
    }

    @Override
    public String getContextPath() {
        return contextPath;
    }

    @Override
    public void setContextPath(String contextPath) {
        this.contextPath = contextPath;
    }
}
